package cn.gov.mwr.sl651;

import java.util.ArrayList;
import java.util.List;

import cn.gov.mwr.sl651.body.DataItem;
import cn.gov.mwr.sl651.body.Up40Body;
import cn.gov.mwr.sl651.body.Up41Body;
import cn.gov.mwr.sl651.body.Up42Body;
import cn.gov.mwr.sl651.body.Up44Body;
import cn.gov.mwr.sl651.body.Up45Body;
import cn.gov.mwr.sl651.body.Up46Body;
import cn.gov.mwr.sl651.body.Up47Body;
import cn.gov.mwr.sl651.body.Up48Body;
import cn.gov.mwr.sl651.body.Up49Body;
import cn.gov.mwr.sl651.body.Up4ABody;
import cn.gov.mwr.sl651.body.Up4BBody;
import cn.gov.mwr.sl651.body.Up4CBody;
import cn.gov.mwr.sl651.body.Up4DBody;
import cn.gov.mwr.sl651.body.Up4EBody;
import cn.gov.mwr.sl651.body.Up4FBody;
import cn.gov.mwr.sl651.body.Up50Body;
import cn.gov.mwr.sl651.body.Up51Body;
import cn.gov.mwr.sl651.body.UpBaseBody;
import cn.gov.mwr.sl651.utils.ByteUtil;
import cn.gov.mwr.sl651.utils.StcdParser;

public abstract class AbstractParser implements IParser {

	/**
	 * 解析数据长度
	 * @param bodyLen
	 * @return
	 */
	public static int parseLength(byte[] bodyLen) {
		byte A1 = (byte) (bodyLen[0] & 0x0f);
		byte A2 = bodyLen[1];
		return ByteUtil.bytesToUshort(new byte[] { A1, A2 });
	}
	
	/**
	 * 解析报文的模式，相当于只在此解析为M2,M3模式
	 * 
	 * 注意点：M1模式在外部解析，M4模式不做解析
	 * 
	 * @param data
	 */
	public String parseMode(IMessageHeader header) {
		String mode = "M2";

		// 根据功能码判定为M1模式，功能码在外层做判断，不进入本层判断
		// byte funcCode = parseFuncCode(header.getFuncCode());
		// if (funcCode == 0x2F) {
		// mode = "M1";
		// }

		// 根据报文起始符判定为使用M2或M3模式
		byte[] bodyStartBit = header.getBodyStartBit();
		if (bodyStartBit[0] == Symbol.SYN) {
			mode = "M3";
		} else {
			mode = "M2";
		}

		return mode;
	}

	/**
	 * 构造“报文结束符”
	 * 
	 * @param mode
	 * @param bodyEndBit
	 * @param keep
	 * @return
	 */
	public static byte parseEndBit(String mode, byte bodyEndBit, boolean keep) {

		byte replyStartBit = Symbol.EOT;

		if (mode.equals("M2")) {
			// 关键点：针对ETB方式，需多次回复确认
			// 其上行帧报文结束符为ETB/ETX
			// 下行帧为“确认”帧，报文结束符为EOT/ESC。
			if (bodyEndBit == Symbol.ETB) {
				replyStartBit = Symbol.ACK;
			}
			if (bodyEndBit == Symbol.ETX) {
				if (keep == true) {
					replyStartBit = Symbol.ESC;// 保持在线
				} else {
					replyStartBit = Symbol.EOT;
				}
			}
		}
		if (mode.equals("M3")) {
			// 关键点：仅回复一次确认包
			// 其上行帧报文结束符为ETB/ETX（收到NAK的重发包用ETX）；
			// 下行帧为“确认/否认”帧，报文结束符为EOT/NAK/ESC

			// ----data------第一帧报文ETB-------
			// .................................|
			// ----data------第二帧报文ETB-------
			// .................................|
			// ----data------第三帧报文ETB-------
			// .................................|
			// ----data------最后一帧报文ETX-------
			// --正确，则发EOT确认;错误，则发送NAK结束
			if (bodyEndBit == Symbol.ETB) {
				// replyStartBit = null;
			}

			if (bodyEndBit == Symbol.ETX) {
				if (keep == true) {
					replyStartBit = Symbol.ESC;// 保持在线
				} else {
					replyStartBit = Symbol.EOT;
				}
			}

		}
		if (mode.equals("M4")) {
			// 关键点：其实M4模式就是M2模式与M3模式的结合。发起端是中心站，接收到的数据不存在此种模式
			// 下行帧为“查询/确认”帧，报文结束符为ENQ/ACK/EOT；
			// 上行帧为响应帧，报文结束符为ETB/ETX。

		}

		// 总结：
		// --------报文起始符，只有两种STX/SYN。SYN只有在多包传输正文时使用
		// ----上行报文结束符，只有两种ETB/ETX。ETB后续有报文（与起始符SYN结合使用），ETX后续无报文

		return replyStartBit;
	}

	// --------------------------------------------------------------------
	//
	//
	// ---------------------------------------------------------------------

	/**
	 * 6.6.4.16　中心站修改遥测站运行参数配置表
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up42Body parse42Body(byte[] bytes) throws Exception {

		Up42Body body = new Up42Body();

		int pos = 0;
		// 序列号
		byte[] serialByte = new byte[2];
		System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
		pos = pos + serialByte.length;
		body.setSerialId(ByteUtil.bytesToUshort(serialByte));

		// 发报时间
		byte[] sendDateByte = new byte[6];
		System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
		body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
		pos = pos + sendDateByte.length;

		byte[] stationSymbol = new byte[2];
		System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
		pos = pos + stationSymbol.length;

		byte[] stationAddr = new byte[5];
		System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);
		body.setStcd(parseStcd(stationAddr));
		pos = pos + stationAddr.length;

		// 数据内容
		int len = bytes.length - pos;
		byte[] data = new byte[len];
		System.arraycopy(bytes, pos, data, 0, len);

		List<DataItem> itemList = new ArrayList<DataItem>();

		// 开始读取业务数据
		for (int i = 0, ln = data.length; i < ln;) {
			byte flag = data[i];
			i = i + 1;
			byte dataLenFlag = data[i];
			i = i + 1;
			int tempDataLen = (int) (dataLenFlag >> 3 & 0x1F);// 获取前五位的数据
			int tempdecimal = (int) (dataLenFlag & 0x07); // 获取后三位的小数点位数

			byte[] temp = new byte[tempDataLen];
			System.arraycopy(data, i, temp, 0, tempDataLen);

//			System.out.println(">>运行参数 -----标识符-- "
//					+ ByteUtil.toHexString(flag) + " -----开始读取" + tempDataLen
//					+ "个字节---------tempdecimal " + tempdecimal + " value "
//					+ ByteUtil.bcd2Str(temp));

			DataItem item = new DataItem();
			item.setLength(tempDataLen);
			item.setDecimal(tempdecimal);
			item.setLabel(new byte[] { flag,dataLenFlag });
			item.setValue(temp);
			itemList.add(item);

			i = i + tempDataLen;
		}
		body.setItems(itemList);

		return body;
	}

	/**
	 * 6.6.4.19　中心站查询遥测站软件版本
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up45Body parse45Body(byte[] bytes) throws Exception {
		Up45Body body = new Up45Body();

		int pos = 0;
		// 序列号
		byte[] serialByte = new byte[2];
		System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
		pos = pos + serialByte.length;
		body.setSerialId(ByteUtil.bytesToUshort(serialByte));

		// 发报时间
		byte[] sendDateByte = new byte[6];
		System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
		body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
		pos = pos + sendDateByte.length;

		byte[] stationSymbol = new byte[2];
		System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
		pos = pos + stationSymbol.length;

		byte[] stationAddr = new byte[5];
		System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);
		body.setStcd(parseStcd(stationAddr));
		pos = pos + stationAddr.length;

		byte[] datalen = new byte[1];
		System.arraycopy(bytes, pos, datalen, 0, datalen.length);
		body.setDatalen(datalen[0]);
		pos = pos + datalen.length;

		int count = ByteUtil.bytesToUbyte(datalen);
		byte[] data = new byte[count];
		System.arraycopy(bytes, pos, data, 0, data.length);
		body.setData(data);

		return body;
	}

	/**
	 * 6.6.4.20　中心站查询遥测站状态和报警信息
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up46Body parse46Body(byte[] bytes) throws Exception {
		Up46Body body = new Up46Body();

		int pos = 0;

		// 序列号
		byte[] serialByte = new byte[2];
		System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
		pos = pos + serialByte.length;
		body.setSerialId(ByteUtil.bytesToUshort(serialByte));

		// 发报时间
		byte[] sendDateByte = new byte[6];
		System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
		body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
		pos = pos + sendDateByte.length;

		byte[] stationSymbol = new byte[2];
		System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
		pos = pos + stationSymbol.length;

		byte[] stationAddr = new byte[5];
		System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);
		body.setStcd(parseStcd(stationAddr));
		pos = pos + stationAddr.length;

		byte[] symbol = new byte[2];
		System.arraycopy(bytes, pos, symbol, 0, symbol.length);
		body.setSymbol(symbol);
		pos = pos + symbol.length;
		
		byte[] data = new byte[4];
		System.arraycopy(bytes, pos, data, 0, data.length);
		body.setData(data);

		System.out.println(">>>>>>>>>> " +ByteUtil.toHexString(data) );
		
		return body;
	}

	/**
	 * 6.6.4.21　初始化固态存储数据
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up47Body parse47Body(byte[] bytes) throws Exception {
		return (Up47Body) parseUpBaseBody(bytes);
	}

	/**
	 * 6.6.4.22　恢复遥测站出厂设置
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up48Body parse48Body(byte[] bytes) throws Exception {
		return (Up48Body) parseUpBaseBody(bytes);
	}

	/**
	 * 6.6.4.23　修改密码
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up49Body parse49Body(byte[] bytes) throws Exception {

		Up49Body body = new Up49Body();

		int pos = 0;

		// 序列号
		byte[] serialByte = new byte[2];
		System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
		pos = pos + serialByte.length;
		body.setSerialId(ByteUtil.bytesToUshort(serialByte));

		// 发报时间
		byte[] sendDateByte = new byte[6];
		System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
		body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
		pos = pos + sendDateByte.length;

		byte[] stationSymbol = new byte[2];
		System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
		pos = pos + stationSymbol.length;

		byte[] stationAddr = new byte[5];
		System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);
		body.setStcd(parseStcd(stationAddr));
		pos = pos + stationAddr.length;

		byte[] symbol = new byte[2];
		System.arraycopy(bytes, pos, symbol, 0, symbol.length);
		body.setSymbol(symbol);
		pos = pos + symbol.length;

		byte[] pwd = new byte[2];
		System.arraycopy(bytes, pos, pwd, 0, pwd.length);
		body.setPwd(pwd);
		pos = pos + symbol.length;
		
		return body;
	}

	/**
	 * 6.6.4.24　设置遥测站时钟
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public UpBaseBody parse4ABody(byte[] bytes) throws Exception {
		return (UpBaseBody) parseUpBaseBody(bytes);
	}

	/**
	 * 6.6.4.25　设置遥测站IC卡状态
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up4BBody parse4BBody(byte[] bytes) throws Exception {
		return (Up4BBody) parse46Body(bytes);
	}

	/**
	 * 6.6.4.26　控制水泵开关命令/水泵状态自报
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up4CBody parse4CBody(byte[] bytes) throws Exception {
		Up4CBody body = new Up4CBody();

		int pos = 0;

		// 序列号
		byte[] serialByte = new byte[2];
		System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
		pos = pos + serialByte.length;
		body.setSerialId(ByteUtil.bytesToUshort(serialByte));

		// 发报时间
		byte[] sendDateByte = new byte[6];
		System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
		body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
		pos = pos + sendDateByte.length;

		byte[] stationSymbol = new byte[2];
		System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
		pos = pos + stationSymbol.length;

		byte[] stationAddr = new byte[5];
		System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);
		body.setStcd(parseStcd(stationAddr));
		pos = pos + stationAddr.length;

		byte[] datalen = new byte[1];
		System.arraycopy(bytes, pos, datalen, 0, datalen.length);
		body.setDatalen(datalen[0]);
		pos = pos + datalen.length;

		int len = ByteUtil.bytesToUbyte(datalen);
		byte[] data = new byte[len];
		System.arraycopy(bytes, pos, data, 0, data.length);
		body.setData(data);

		return body;
	}


	/**
	 * 6.6.4.28　控制闸门开关命令/闸门状态信息自报
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up4EBody parse4EBody(byte[] bytes) throws Exception {
		Up4EBody body = new Up4EBody();

		int pos = 0;

		// 序列号
		byte[] serialByte = new byte[2];
		System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
		pos = pos + serialByte.length;
		body.setSerialId(ByteUtil.bytesToUshort(serialByte));

		// 发报时间
		byte[] sendDateByte = new byte[6];
		System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
		body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
		pos = pos + sendDateByte.length;

		byte[] stationSymbol = new byte[2];
		System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
		pos = pos + stationSymbol.length;

		byte[] stationAddr = new byte[5];
		System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);
		body.setStcd(parseStcd(stationAddr));
		pos = pos + stationAddr.length;

		byte[] countBytes = new byte[1];
		System.arraycopy(bytes, pos, countBytes, 0, countBytes.length);
		int count = ByteUtil.bytesToUbyte(countBytes);
		body.setCount(count);
		pos = pos + countBytes.length;

		int dataLen = count / 8;
		dataLen = dataLen + ((count % 8) != 0 ? 1 : 0);
		byte[] data = new byte[dataLen];
		System.arraycopy(bytes, pos, data, 0, data.length);
		body.setData(data);
		pos = pos + data.length;

		byte[] kddata = new byte[count * 2];
		System.arraycopy(bytes, pos, kddata, 0, kddata.length);
		body.setKd(kddata);
		pos = pos + kddata.length;

		return body;
	}

	/**
	 * 6.6.4.29　水量定值控制命令
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up4FBody parse4FBody(byte[] bytes) throws Exception {
		Up4FBody body = new Up4FBody();

		int pos = 0;

		// 序列号
		byte[] serialByte = new byte[2];
		System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
		pos = pos + serialByte.length;
		body.setSerialId(ByteUtil.bytesToUshort(serialByte));

		// 发报时间
		byte[] sendDateByte = new byte[6];
		System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
		body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
		pos = pos + sendDateByte.length;

		byte[] stationSymbol = new byte[2];
		System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
		pos = pos + stationSymbol.length;

		byte[] stationAddr = new byte[5];
		System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);
		body.setStcd(parseStcd(stationAddr));
		pos = pos + stationAddr.length;

		byte[] data = new byte[1];
		System.arraycopy(bytes, pos, data, 0, data.length);
		body.setData(data[0]);

		return body;
	}

	/**
	 * 6.6.4.30　中心站查询遥测站事件记录
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up50Body parse50Body(byte[] bytes) throws Exception {
		Up50Body body = new Up50Body();
		int pos = 0;

		// 序列号
		byte[] serialByte = new byte[2];
		System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
		pos = pos + serialByte.length;
		body.setSerialId(ByteUtil.bytesToUshort(serialByte));

		// 发报时间
		byte[] sendDateByte = new byte[6];
		System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
		body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
		pos = pos + sendDateByte.length;

		byte[] stationSymbol = new byte[2];
		System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
		pos = pos + stationSymbol.length;

		byte[] stationAddr = new byte[5];
		System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);
		body.setStcd(parseStcd(stationAddr));
		pos = pos + stationAddr.length;

		byte[] erc = new byte[64];
		System.arraycopy(bytes, pos, erc, 0, erc.length);
		body.setErc(erc);

		return body;
	}

	/**
	 * 6.6.4.31　中心站查询遥测站时钟
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public Up51Body parse51Body(byte[] bytes) throws Exception {
		return (Up51Body) parseUpBaseBody(bytes);
	}

	/**
	 * 基础类
	 * 
	 * @param messageBody
	 * @return
	 * @throws Exception
	 */
	public UpBaseBody parseUpBaseBody(byte[] bytes)
			throws Exception {
		UpBaseBody body = new UpBaseBody();
		int pos = 0;

		// 序列号
		byte[] serialByte = new byte[2];
		System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
		pos = pos + serialByte.length;
		body.setSerialId(ByteUtil.bytesToUshort(serialByte));

		// 发报时间
		byte[] sendDateByte = new byte[6];
		System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
		body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
		pos = pos + sendDateByte.length;

		byte[] stationSymbol = new byte[2];
		System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
		pos = pos + stationSymbol.length;

		byte[] stationAddr = new byte[5];
		System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);
		pos = pos + stationAddr.length;
		body.setStcd(parseStcd(stationAddr));

		return body;
	}

}
